home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / Source / Chapter 8 / Engine / Mesh.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2005-03-29  |  19.6 KB  |  523 lines

  1. //-----------------------------------------------------------------------------
  2. // Mesh.h implementation.
  3. // Refer to the Mesh.h interface for more details.
  4. //
  5. // Programming a Multiplayer First Person Shooter in DirectX
  6. // Copyright (c) 2004 Vaughan Young
  7. //-----------------------------------------------------------------------------
  8. #include "Engine.h"
  9.  
  10. //-----------------------------------------------------------------------------
  11. // Creates a new frame.
  12. //-----------------------------------------------------------------------------
  13. HRESULT AllocateHierarchy::CreateFrame( THIS_ LPCSTR Name, LPD3DXFRAME *ppNewFrame )
  14. {
  15.     // Create the new frame and zero its memory.
  16.     Frame *frame = new Frame;
  17.     ZeroMemory( frame, sizeof( Frame ) );
  18.  
  19.     // Copy the frame's name.
  20.     if( Name == NULL )
  21.     {
  22.         // There is no name, so create a unique name.
  23.         static unsigned long nameCount = 0;
  24.         char newName[32];
  25.         sprintf( newName, "unknown_frame_%d", nameCount );
  26.         nameCount++;
  27.  
  28.         frame->Name = new char[strlen( newName ) + 1];
  29.         strcpy( frame->Name, newName );
  30.     }
  31.     else
  32.     {
  33.         frame->Name = new char[strlen( Name ) + 1];
  34.         strcpy( frame->Name, Name );
  35.     }
  36.  
  37.     *ppNewFrame = frame;
  38.  
  39.     return S_OK;
  40. }
  41.  
  42. //-----------------------------------------------------------------------------
  43. // Creates a new mesh container.
  44. //-----------------------------------------------------------------------------
  45. HRESULT AllocateHierarchy::CreateMeshContainer( THIS_ LPCSTR Name, CONST D3DXMESHDATA *pMeshData, CONST D3DXMATERIAL *pMaterials, CONST D3DXEFFECTINSTANCE *pEffectInstances, DWORD NumMaterials, CONST DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppNewMeshContainer )
  46. {
  47.     // Create the new mesh container and zero its memory.
  48.     MeshContainer *meshContainer = new MeshContainer;
  49.     ZeroMemory( meshContainer, sizeof( MeshContainer ) );
  50.  
  51.     // Copy the mesh's name.
  52.     if( Name == NULL )
  53.     {
  54.         // There is no name, so create a unique name.
  55.         static unsigned long nameCount = 0;
  56.         char newName[32];
  57.         sprintf( newName, "unknown_mesh_%d", nameCount );
  58.         nameCount++;
  59.  
  60.         meshContainer->Name = new char[strlen( newName ) + 1];
  61.         strcpy( meshContainer->Name, newName );
  62.     }
  63.     else
  64.     {
  65.         meshContainer->Name = new char[strlen( Name ) + 1];
  66.         strcpy( meshContainer->Name, Name );
  67.     }
  68.  
  69.     // Check if the mesh has any materials.
  70.     if( ( meshContainer->NumMaterials = NumMaterials ) > 0 )
  71.     {
  72.         // Allocate some memory for the mesh's materials, and their names (i.e. texture names).
  73.         meshContainer->materials = new Material*[meshContainer->NumMaterials];
  74.         meshContainer->materialNames = new char*[meshContainer->NumMaterials];
  75.  
  76.         // Store all the material (texture) names.
  77.         for( unsigned long m = 0; m < NumMaterials; m++ )
  78.         {
  79.             if( pMaterials[m].pTextureFilename )
  80.             {
  81.                 meshContainer->materialNames[m] = new char[strlen( pMaterials[m].pTextureFilename ) + 1];
  82.                 memcpy( meshContainer->materialNames[m], pMaterials[m].pTextureFilename, ( strlen( pMaterials[m].pTextureFilename ) + 1 ) * sizeof( char ) );
  83.             }
  84.             else
  85.                 meshContainer->materialNames[m] = NULL;
  86.  
  87.             meshContainer->materials[m] = NULL;
  88.         }
  89.     }
  90.  
  91.     // Store the mesh's adjacency information.
  92.     meshContainer->pAdjacency = new DWORD[pMeshData->pMesh->GetNumFaces() * 3];
  93.     memcpy( meshContainer->pAdjacency, pAdjacency, sizeof( DWORD ) * pMeshData->pMesh->GetNumFaces() * 3 );
  94.  
  95.     // Store the mesh data.
  96.     meshContainer->MeshData.pMesh = meshContainer->originalMesh = pMeshData->pMesh;
  97.     meshContainer->MeshData.Type = D3DXMESHTYPE_MESH;
  98.     pMeshData->pMesh->AddRef();
  99.     pMeshData->pMesh->AddRef();
  100.  
  101.     // Check if this mesh is a skinned mesh.
  102.     if( pSkinInfo != NULL )
  103.     {
  104.         // Store the skin information and the mesh data.
  105.         meshContainer->pSkinInfo = pSkinInfo;
  106.         pSkinInfo->AddRef();
  107.  
  108.         // Clone the original mesh to create the skinned mesh.
  109.         meshContainer->originalMesh->CloneMeshFVF( D3DXMESH_MANAGED, meshContainer->originalMesh->GetFVF(), g_engine->GetDevice(), &meshContainer->MeshData.pMesh );
  110.  
  111.         // Store the attribute table.
  112.         meshContainer->MeshData.pMesh->GetAttributeTable( NULL, &meshContainer->totalAttributeGroups );
  113.         meshContainer->attributeTable = new D3DXATTRIBUTERANGE[meshContainer->totalAttributeGroups];
  114.         meshContainer->MeshData.pMesh->GetAttributeTable( meshContainer->attributeTable, NULL );
  115.     }
  116.  
  117.     *ppNewMeshContainer = meshContainer;
  118.  
  119.     return S_OK;
  120. }
  121.  
  122. //-----------------------------------------------------------------------------
  123. // Destroys the given frame.
  124. //-----------------------------------------------------------------------------
  125. HRESULT AllocateHierarchy::DestroyFrame( THIS_ LPD3DXFRAME pFrameToFree )
  126. {
  127.     SAFE_DELETE_ARRAY( pFrameToFree->Name );
  128.     SAFE_DELETE( pFrameToFree );
  129.  
  130.     return S_OK;
  131. }
  132.  
  133. //-----------------------------------------------------------------------------
  134. // Destroys the given mesh conatiner.
  135. //-----------------------------------------------------------------------------
  136. HRESULT AllocateHierarchy::DestroyMeshContainer( THIS_ LPD3DXMESHCONTAINER pMeshContainerToFree )
  137. {
  138.     MeshContainer *meshContainer = (MeshContainer*)pMeshContainerToFree;
  139.  
  140.     // Remove all of the mesh's materials from the material manager.
  141.     for( unsigned long m = 0; m < meshContainer->NumMaterials; m++ )
  142.         if( meshContainer->materials )
  143.             g_engine->GetMaterialManager()->Remove( &meshContainer->materials[m] );
  144.  
  145.     // Destroy the mesh container.
  146.     SAFE_DELETE_ARRAY( meshContainer->Name );
  147.     SAFE_DELETE_ARRAY( meshContainer->pAdjacency );
  148.     SAFE_DELETE_ARRAY( meshContainer->pMaterials );
  149.     SAFE_DELETE_ARRAY( meshContainer->materialNames );
  150.     SAFE_DELETE_ARRAY( meshContainer->materials );
  151.     SAFE_DELETE_ARRAY( meshContainer->boneMatrixPointers );
  152.     SAFE_DELETE_ARRAY( meshContainer->attributeTable );
  153.     SAFE_RELEASE( meshContainer->MeshData.pMesh );
  154.     SAFE_RELEASE( meshContainer->pSkinInfo );
  155.     SAFE_RELEASE( meshContainer->originalMesh );
  156.     SAFE_DELETE( meshContainer );
  157.  
  158.     return S_OK;
  159. }
  160.  
  161. //-----------------------------------------------------------------------------
  162. // The mesh class constructor.
  163. //-----------------------------------------------------------------------------
  164. Mesh::Mesh( char *name, char *path ) : Resource< Mesh >( name, path )
  165. {
  166.     // Create the list of reference points.
  167.     m_frames = new LinkedList< Frame >;
  168.     m_refPoints = new LinkedList< Frame >;
  169.  
  170.     // Load the mesh's frame hierarchy.
  171.     AllocateHierarchy ah;
  172.     D3DXLoadMeshHierarchyFromX( GetFilename(), D3DXMESH_MANAGED, g_engine->GetDevice(), &ah, NULL, (D3DXFRAME**)&m_firstFrame, &m_animationController );
  173.  
  174.     // Disable all the animation tracks initially.
  175.     if( m_animationController != NULL )
  176.         for( unsigned long t = 0; t < m_animationController->GetMaxNumTracks(); ++t )
  177.             m_animationController->SetTrackEnable( t, false );
  178.  
  179.     // Invalidate the bone transformation matrices array.
  180.     m_boneMatrices = NULL;
  181.     m_totalBoneMatrices = 0;
  182.  
  183.     // Prepare the frame hierarchy.
  184.     PrepareFrame( m_firstFrame );
  185.  
  186.     // Allocate memory for the bone matrices.
  187.     m_boneMatrices = new D3DXMATRIX[m_totalBoneMatrices];
  188.  
  189.     // Create a static (non-animated) version of the mesh.
  190.     m_staticMesh = new MeshContainer;
  191.     ZeroMemory( m_staticMesh, sizeof( MeshContainer ) );
  192.  
  193.     // Load the mesh.
  194.     ID3DXBuffer *materialBuffer, *adjacencyBuffer;
  195.     D3DXLoadMeshFromX( GetFilename(), D3DXMESH_MANAGED, g_engine->GetDevice(), &adjacencyBuffer, &materialBuffer, NULL, &m_staticMesh->NumMaterials, &m_staticMesh->originalMesh );
  196.  
  197.     // Optimise the mesh for better rendering performance.
  198.     m_staticMesh->originalMesh->OptimizeInplace( D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)adjacencyBuffer->GetBufferPointer(), NULL, NULL, NULL );
  199.  
  200.     // Finished with the adjacency buffer, so destroy it.
  201.     SAFE_RELEASE( adjacencyBuffer );
  202.  
  203.     // Check if the mesh has any materials.
  204.     if( m_staticMesh->NumMaterials > 0 )
  205.     {
  206.         // Create the array of materials.
  207.         m_staticMesh->materials = new Material*[m_staticMesh->NumMaterials];
  208.  
  209.         // Get the list of materials from the material buffer.
  210.         D3DXMATERIAL *materials = (D3DXMATERIAL*)materialBuffer->GetBufferPointer();
  211.  
  212.         // Load each material into the array via the material manager.
  213.         for( unsigned long m = 0; m < m_staticMesh->NumMaterials; m++ )
  214.         {
  215.             // Ensure the material has a texture.
  216.             if( materials[m].pTextureFilename )
  217.             {
  218.                 // Get the name of the material's script and load it.
  219.                 char *name = new char[strlen( materials[m].pTextureFilename ) + 5];
  220.                 sprintf( name, "%s.txt", materials[m].pTextureFilename );
  221.                 m_staticMesh->materials[m] = g_engine->GetMaterialManager()->Add( name, GetPath() );
  222.                 SAFE_DELETE_ARRAY( name );
  223.             }
  224.             else
  225.                 m_staticMesh->materials[m] = NULL;
  226.         }
  227.     }
  228.  
  229.     // Create the bounding volume around the mesh.
  230.     BoundingVolumeFromMesh( m_staticMesh->originalMesh );
  231.  
  232.     // Destroy the material buffer.
  233.     SAFE_RELEASE( materialBuffer );
  234.  
  235.     // Create a vertex array and an array of indices into the vertex array.
  236.     m_vertices = new Vertex[m_staticMesh->originalMesh->GetNumVertices()];
  237.     m_indices = new unsigned short[m_staticMesh->originalMesh->GetNumFaces() * 3];
  238.  
  239.     // Use the arrays to store a local copy of the static mesh's vertices and
  240.     // indices so that they can be used by the scene manager on the fly.
  241.     Vertex* verticesPtr;
  242.     m_staticMesh->originalMesh->LockVertexBuffer( 0, (void**)&verticesPtr );
  243.     unsigned short *indicesPtr;
  244.     m_staticMesh->originalMesh->LockIndexBuffer( 0, (void**)&indicesPtr );
  245.  
  246.     memcpy( m_vertices, verticesPtr, VERTEX_FVF_SIZE * m_staticMesh->originalMesh->GetNumVertices() );
  247.     memcpy( m_indices, indicesPtr, sizeof( unsigned short ) * m_staticMesh->originalMesh->GetNumFaces() * 3 );
  248.  
  249.     m_staticMesh->originalMesh->UnlockVertexBuffer();
  250.     m_staticMesh->originalMesh->UnlockIndexBuffer();
  251. }
  252.  
  253. //-----------------------------------------------------------------------------
  254. // The mesh class destructor.
  255. //-----------------------------------------------------------------------------
  256. Mesh::~Mesh()
  257. {
  258.     // Destroy the frame hierarchy.
  259.     AllocateHierarchy ah;
  260.     D3DXFrameDestroy( m_firstFrame, &ah );
  261.  
  262.     // Destroy the frames list and reference points list.
  263.     m_frames->ClearPointers();
  264.     SAFE_DELETE( m_frames );
  265.     m_refPoints->ClearPointers();
  266.     SAFE_DELETE( m_refPoints );
  267.  
  268.     // Release the animation controller.
  269.     SAFE_RELEASE( m_animationController );
  270.  
  271.     // Destroy the bone matrices.
  272.     SAFE_DELETE_ARRAY( m_boneMatrices );
  273.  
  274.     // Destroy the static mesh.
  275.     if( m_staticMesh )
  276.     {
  277.         // Remove all the static mesh's textures.
  278.         for( unsigned long m = 0; m < m_staticMesh->NumMaterials; m++ )
  279.             if( m_staticMesh->materials )
  280.                 g_engine->GetMaterialManager()->Remove( &m_staticMesh->materials[m] );
  281.  
  282.         // Clean up the rest of it.
  283.         SAFE_DELETE_ARRAY( m_staticMesh->materials );
  284.         SAFE_RELEASE( m_staticMesh->originalMesh );
  285.         SAFE_DELETE( m_staticMesh );
  286.     }
  287.  
  288.     // Destroy the vertex and index arrays.
  289.     SAFE_DELETE_ARRAY( m_vertices );
  290.     SAFE_DELETE_ARRAY( m_indices );
  291. }
  292.  
  293. //-----------------------------------------------------------------------------
  294. // Updates the mesh.
  295. //-----------------------------------------------------------------------------
  296. void Mesh::Update()
  297. {
  298.     UpdateFrame( m_firstFrame );
  299. }
  300.  
  301. //-----------------------------------------------------------------------------
  302. // Renders the mesh.
  303. //-----------------------------------------------------------------------------
  304. void Mesh::Render()
  305. {
  306.     RenderFrame( m_firstFrame );
  307. }
  308.  
  309. //-----------------------------------------------------------------------------
  310. // Create a clone of the mesh's animation controller.
  311. //-----------------------------------------------------------------------------
  312. void Mesh::CloneAnimationController( ID3DXAnimationController **animationController )
  313. {
  314.     if( m_animationController )
  315.         m_animationController->CloneAnimationController( m_animationController->GetMaxNumAnimationOutputs(), m_animationController->GetMaxNumAnimationSets(), m_animationController->GetMaxNumTracks(), m_animationController->GetMaxNumEvents(), &*animationController );
  316.     else
  317.         *animationController = NULL;
  318. }
  319.  
  320. //-----------------------------------------------------------------------------
  321. // Returns the static (non-animated) version of the mesh.
  322. //-----------------------------------------------------------------------------
  323. MeshContainer *Mesh::GetStaticMesh()
  324. {
  325.     return m_staticMesh;
  326. }
  327.  
  328. //-----------------------------------------------------------------------------
  329. // Returns the mesh's static (non-animated) vertices.
  330. //-----------------------------------------------------------------------------
  331. Vertex *Mesh::GetVertices()
  332. {
  333.     return m_vertices;
  334. }
  335.  
  336. //-----------------------------------------------------------------------------
  337. // Returns the mesh's face indices for the vertices.
  338. //-----------------------------------------------------------------------------
  339. unsigned short *Mesh::GetIndices()
  340. {
  341.     return m_indices;
  342. }
  343.  
  344. //-----------------------------------------------------------------------------
  345. // Returns the list of frames in the mesh.
  346. //-----------------------------------------------------------------------------
  347. LinkedList< Frame > *Mesh::GetFrameList()
  348. {
  349.     return m_frames;
  350. }
  351.  
  352. //-----------------------------------------------------------------------------
  353. // Returns the frame with the given name.
  354. //-----------------------------------------------------------------------------
  355. Frame *Mesh::GetFrame( char *name )
  356. {
  357.     m_frames->Iterate( true );
  358.     while( m_frames->Iterate() )
  359.         if( strcmp( m_frames->GetCurrent()->Name, name ) == 0 )
  360.             return m_frames->GetCurrent();
  361.  
  362.     return NULL;
  363. }
  364.  
  365. //-----------------------------------------------------------------------------
  366. // Returns the reference point wth the given name.
  367. //-----------------------------------------------------------------------------
  368. Frame *Mesh::GetReferencePoint( char *name )
  369. {
  370.     m_refPoints->Iterate( true );
  371.     while( m_refPoints->Iterate() )
  372.         if( strcmp( m_refPoints->GetCurrent()->Name, name ) == 0 )
  373.             return m_refPoints->GetCurrent();
  374.  
  375.     return NULL;
  376. }
  377.  
  378. //-----------------------------------------------------------------------------
  379. // Prepares the given frame.
  380. //-----------------------------------------------------------------------------
  381. void Mesh::PrepareFrame( Frame *frame )
  382. {
  383.     m_frames->Add( frame );
  384.  
  385.     // Check if this frame is actually a reference point.
  386.     if( strncmp( "rp_", frame->Name, 3 ) == 0 )
  387.         m_refPoints->Add( frame );
  388.  
  389.     // Set the initial final transformation.
  390.     frame->finalTransformationMatrix = frame->TransformationMatrix;
  391.  
  392.     // Prepare the frame's mesh container, if it has one.
  393.     if( frame->pMeshContainer != NULL )
  394.     {
  395.         MeshContainer *meshContainer = (MeshContainer*)frame->pMeshContainer;
  396.  
  397.         // Check if this mesh is a skinned mesh.
  398.         if( meshContainer->pSkinInfo != NULL )
  399.         {
  400.             // Create the array of bone matrix pointers.
  401.             meshContainer->boneMatrixPointers = new D3DXMATRIX*[meshContainer->pSkinInfo->GetNumBones()];
  402.  
  403.             // Set up the pointers to the mesh's bone transformation matrices.
  404.             for( unsigned long b = 0; b < meshContainer->pSkinInfo->GetNumBones(); b++ )
  405.             {
  406.                 Frame *bone = (Frame*)D3DXFrameFind( m_firstFrame, meshContainer->pSkinInfo->GetBoneName( b ) );
  407.                 if( bone == NULL )
  408.                     continue;
  409.  
  410.                 meshContainer->boneMatrixPointers[b] = &bone->finalTransformationMatrix;
  411.             }
  412.  
  413.             // Keep track of the maximum bones out of all the mesh containers.
  414.             if( m_totalBoneMatrices < meshContainer->pSkinInfo->GetNumBones() )
  415.                 m_totalBoneMatrices = meshContainer->pSkinInfo->GetNumBones();
  416.         }
  417.  
  418.         // Check if the mesh has any materials.
  419.         if( meshContainer->NumMaterials > 0 )
  420.         {
  421.             // Load all the materials in via the material manager.
  422.             for( unsigned long m = 0; m < meshContainer->NumMaterials; m++ )
  423.             {
  424.                 // Ensure the material has a texture.
  425.                 if( meshContainer->materialNames[m] != NULL )
  426.                 {
  427.                     // Get the name of the material's script and load it.
  428.                     char *name = new char[strlen( meshContainer->materialNames[m] ) + 5];
  429.                     sprintf( name, "%s.txt", meshContainer->materialNames[m] );
  430.                     meshContainer->materials[m] = g_engine->GetMaterialManager()->Add( name, GetPath() );
  431.                     SAFE_DELETE_ARRAY( name );
  432.                 }
  433.             }
  434.         }
  435.     }
  436.  
  437.     // Prepare the frame's siblings.
  438.     if( frame->pFrameSibling != NULL )
  439.         PrepareFrame( (Frame*)frame->pFrameSibling );
  440.  
  441.     // Prepare the frame's children.
  442.     if( frame->pFrameFirstChild != NULL )
  443.         PrepareFrame( (Frame*)frame->pFrameFirstChild );
  444. }
  445.  
  446. //-----------------------------------------------------------------------------
  447. // Updates the given frame's transformation matrices.
  448. //-----------------------------------------------------------------------------
  449. void Mesh::UpdateFrame( Frame *frame, D3DXMATRIX *parentTransformationMatrix )
  450. {
  451.     if( parentTransformationMatrix != NULL )
  452.         D3DXMatrixMultiply( &frame->finalTransformationMatrix, &frame->TransformationMatrix, parentTransformationMatrix );
  453.     else
  454.         frame->finalTransformationMatrix = frame->TransformationMatrix;
  455.  
  456.     // Update the frame's siblings.
  457.     if( frame->pFrameSibling != NULL )
  458.         UpdateFrame( (Frame*)frame->pFrameSibling, parentTransformationMatrix );
  459.  
  460.     // Update the frame's children.
  461.     if( frame->pFrameFirstChild != NULL )
  462.         UpdateFrame( (Frame*)frame->pFrameFirstChild, &frame->finalTransformationMatrix );
  463. }
  464.  
  465. //-----------------------------------------------------------------------------
  466. // Renders the given frame's mesh containers, if it has any.
  467. //-----------------------------------------------------------------------------
  468. void Mesh::RenderFrame( Frame *frame )
  469. {
  470.     MeshContainer *meshContainer = (MeshContainer*)frame->pMeshContainer;
  471.  
  472.     // Render this frame's mesh, if it has one.
  473.     if( frame->pMeshContainer != NULL )
  474.     {
  475.         // Check if this mesh is a skinned mesh.
  476.         if( meshContainer->pSkinInfo != NULL )
  477.         {
  478.             // Create the bone transformations using the mesh's transformation matrices.
  479.             for( unsigned long b = 0; b < meshContainer->pSkinInfo->GetNumBones(); ++b )
  480.                 D3DXMatrixMultiply( &m_boneMatrices[b], meshContainer->pSkinInfo->GetBoneOffsetMatrix( b ), meshContainer->boneMatrixPointers[b] );
  481.  
  482.             // Update the meshes vertices with the new bone transformation matrices.
  483.             PBYTE sourceVertices, destinationVertices;
  484.             meshContainer->originalMesh->LockVertexBuffer( D3DLOCK_READONLY, (void**)&sourceVertices );
  485.             meshContainer->MeshData.pMesh->LockVertexBuffer( 0, (void**)&destinationVertices );
  486.             meshContainer->pSkinInfo->UpdateSkinnedMesh( m_boneMatrices, NULL, sourceVertices, destinationVertices );
  487.             meshContainer->originalMesh->UnlockVertexBuffer();
  488.             meshContainer->MeshData.pMesh->UnlockVertexBuffer();
  489.  
  490.             // Render the mesh by atrtribute group.
  491.             for( unsigned long a = 0; a < meshContainer->totalAttributeGroups; a++ )
  492.             {
  493.                 g_engine->GetDevice()->SetMaterial( meshContainer->materials[meshContainer->attributeTable[a].AttribId]->GetLighting() );
  494.                 g_engine->GetDevice()->SetTexture( 0, meshContainer->materials[meshContainer->attributeTable[a].AttribId]->GetTexture() );
  495.                 meshContainer->MeshData.pMesh->DrawSubset( meshContainer->attributeTable[a].AttribId );
  496.             }
  497.         }
  498.         else
  499.         {
  500.             // This is not a skinned mesh, so render it like a static mesh.
  501.             for( unsigned long m = 0; m < meshContainer->NumMaterials; m++)
  502.             {
  503.                 if( meshContainer->materials[m] )
  504.                 {
  505.                     g_engine->GetDevice()->SetMaterial( meshContainer->materials[m]->GetLighting() );
  506.                     g_engine->GetDevice()->SetTexture( 0, meshContainer->materials[m]->GetTexture() );
  507.                 }
  508.                 else
  509.                     g_engine->GetDevice()->SetTexture( 0, NULL );
  510.  
  511.                 meshContainer->MeshData.pMesh->DrawSubset( m );
  512.             }
  513.         }
  514.     }
  515.  
  516.     // Render the frame's siblings.
  517.     if( frame->pFrameSibling != NULL )
  518.         RenderFrame( (Frame*)frame->pFrameSibling );
  519.  
  520.     // Render the frame's children.
  521.     if( frame->pFrameFirstChild != NULL )
  522.         RenderFrame( (Frame*)frame->pFrameFirstChild );
  523. }